/*
 *			GPAC - Multimedia Framework C SDK
 *
 *			Authors: Jean Le Feuvre
 *			Copyright (c) Telecom ParisTech 2000-2012
 *					All rights reserved
 *
 *  This file is part of GPAC / common tools sub-project
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  GPAC is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#ifndef _GF_DOWNLOAD_H_
#define _GF_DOWNLOAD_H_

/*!
 *	\file <gpac/download.h>
 *	\brief HTTP(S) Downloader.
 */

/*!
 *	\addtogroup dld_grp Downloader
 *	\ingroup utils_grp
 *	\brief File Downloader objects
 *
 *	This section documents the file downloading tools the GPAC framework. Currently HTTP is supported, HTTPS is under testing but may not be supported
 *depending on GPAC compilation options (HTTPS in GPAC needs OpenSSL installed on the system).
 *
 *	@{
 */


#ifdef __cplusplus
extern "C" {
#endif

#include <gpac/tools.h>
#include <gpac/config_file.h>
#include <gpac/cache.h>


/*!the download manager object. This is usually not used by GPAC modules*/
typedef struct __gf_download_manager GF_DownloadManager;
/*!the download manager session.*/
typedef struct __gf_download_session GF_DownloadSession;

typedef struct GF_URL_Info_Struct {
	const char * protocol;
	char * server_name;
	char * remotePath;
	char * canonicalRepresentation;
	char * userName;
	char * password;
	u16 port;
} GF_URL_Info;

/*!
 * Extracts the information from an URL. A call to gf_dm_url_info_init() must have been issue before calling this method.
 * \param url The URL to fill
 * \param info This structure will be initialized properly and filled with the data
 * \param baseURL The baseURL to use if any (can be null)
 * \return GF_OK if URL is well formed and supported by GPAC
 */
GF_Err gf_dm_get_url_info(const char * url, GF_URL_Info * info, const char * baseURL);

/**
 * Init the GF_URL_Info structure before it can be used
 * \param info The structure to initialize
 */
void gf_dm_url_info_init(GF_URL_Info * info);

/*!
 * Frees the inner structures of a GF_URL_Info_Struct
 * \param info The info to free
 */
void gf_dm_url_info_del(GF_URL_Info * info);

/*!
 *\brief download manager constructor
 *
 *Creates a new download manager object.
 *\param cfg optional configuration file. Currently the download manager needs a configuration file for cache location and
 *other options. The cache directory must be indicated in the section "General", key "CacheDirectory" of the configuration
 *file. If the cache directory is not found, the cache will be disabled but the downloader will still work.
 *\return the download manager object
*/
GF_DownloadManager *gf_dm_new(GF_Config *cfg);
/*
 *\brief download manager destructor
 *
 *Deletes the download manager. All running sessions are aborted
 *\param dm the download manager object
 */
void gf_dm_del(GF_DownloadManager *dm);

/*!
 *\brief callback function for authentication
 *
 * The gf_dm_get_usr_pass type is the type for the callback of the \ref gf_dm_set_auth_callback function used for password retrieval
 *\param usr_cbk opaque user data
 *\param site_url url of the site the user and password are requested for
 *\param usr_name the user name for this site. The allocated space for this buffer is 50 bytes. \note this varaibale may already be formatted.
 *\param password the password for this site and user. The allocated space for this buffer is 50 bytes.
 *\return 0 if user didn't fill in the information which will result in an authentication failure, 1 otherwise.
*/
typedef Bool (*gf_dm_get_usr_pass)(void *usr_cbk, const char *site_url, char *usr_name, char *password);

/*!
 *\brief password retrieval assignment
 *
 *Assigns the callback function used for user password retrieval. If no such function is assigned to the download manager,
 *all downloads requiring authentication will fail.
 *\param dm the download manager object
 *\param get_pass \ref gf_dm_get_usr_pass callback function for user and password retrieval.
 *\param usr_cbk opaque user data passed to callback function
 */
void gf_dm_set_auth_callback(GF_DownloadManager *dm, gf_dm_get_usr_pass get_pass, void *usr_cbk);

/*!downloader session message types*/
typedef enum
{
	/*!signal that session is setup and waiting for connection request*/
	GF_NETIO_SETUP = 0,
	/*!signal that session connection is done*/
	GF_NETIO_CONNECTED,
	/*!request a protocol method from the user. Default value is "GET" for HTTP*/
	GF_NETIO_GET_METHOD,
	/*!request a header from the user. */
	GF_NETIO_GET_HEADER,
	/*!requesting content from the user, if any. Content is appended to the request*/
	GF_NETIO_GET_CONTENT,
	/*!signal that request is sent and waiting for server reply*/
	GF_NETIO_WAIT_FOR_REPLY,
	/*!signal a header to user. */
	GF_NETIO_PARSE_HEADER,
	/*!signal request reply to user. The reply is always sent after the headers*/
	GF_NETIO_PARSE_REPLY,
	/*!send data to the user*/
	GF_NETIO_DATA_EXCHANGE,
	/*!all data has been transfered*/
	GF_NETIO_DATA_TRANSFERED,
	/*!signal that the session has been deconnected*/
	GF_NETIO_DISCONNECTED,
	/*!downloader session failed (error code set) or done/destroyed (no error code)*/
	GF_NETIO_STATE_ERROR
} GF_NetIOStatus;

/*!session download flags*/
typedef enum
{
	/*!session is not threaded, the user must explicitely fetch the data , either with the function gf_dm_sess_fetch_data
	or the function gf_dm_sess_process- if the session is threaded, the user must call gf_dm_sess_process to start the session*/
	GF_NETIO_SESSION_NOT_THREADED = 1,
	/*! session data is cached or not */
	GF_NETIO_SESSION_NOT_CACHED = 1<<1,
	/*! forces data notification even when session is threaded*/
	GF_NETIO_SESSION_NOTIFY_DATA = 1<<2,
	/*indicates that the connection to the server should be kept once the download is successfully completed*/
	GF_NETIO_SESSION_PERSISTENT = 1<<3,
	/*file is stored in memory, and the cache name is set to gpac://%u@%p, where %d is the size in bytes and %d is the the pointer to the memory.
	Memory cached files are destroyed upon downloader destruction*/
	GF_NETIO_SESSION_MEMORY_CACHE = 1<<4,
} GF_NetIOFlags;


/*!protocol I/O parameter*/
typedef struct
{
	/*!parameter message type*/
	GF_NetIOStatus msg_type;
	/*error code if any. Valid for all message types.*/
	GF_Err error;
	/*!data received or data to send. Only valid for GF_NETIO_GET_CONTENT and GF_NETIO_DATA_EXCHANGE (when no cache is setup) messages*/
	const char *data;
	/*!size of associated data. Only valid for GF_NETIO_GET_CONTENT and GF_NETIO_DATA_EXCHANGE messages*/
	u32 size;
	/*protocol header. Only valid for GF_NETIO_GET_HEADER, GF_NETIO_PARSE_HEADER and GF_NETIO_GET_METHOD*/
	const char *name;
	/*protocol header value or server response. Only alid for GF_NETIO_GET_HEADER, GF_NETIO_PARSE_HEADER and GF_NETIO_PARSE_REPLY*/
	char *value;
	/*message-dependend
		for GF_NETIO_PARSE_REPLY, response code
		for GF_NETIO_DATA_EXCHANGE
			Set to 1 in to indicate end of chunk transfer
			Set to 2 in GF_NETIO_DATA_EXCHANGE to indicate complete file is already received (replay of events from cache)
		for all other, usage is reserved
	*/
	u32 reply;
	/*download session for which the message is being sent*/
	GF_DownloadSession *sess;
} GF_NETIO_Parameter;

/*!
 *\brief callback function for data reception and state signaling
 *
 * The gf_dm_user_io type is the type for the data callback function of a download session
 *\param usr_cbk opaque user data
 *\param parameter the input/output parameter structure
*/
typedef void (*gf_dm_user_io)(void *usr_cbk, GF_NETIO_Parameter *parameter);



/*!
 *\brief download session constructor
 *
 *Creates a new download session
 *\param dm the download manager object
 *\param url file to retrieve (no PUT/POST yet, only downloading is supported)
 *\param dl_flags combination of session download flags
 *\param user_io \ref gf_dm_user_io callback function for data reception and service messages
 *\param usr_cbk opaque user data passed to callback function
 *\param error error for failure cases
 *\return the session object or NULL if error. If no error is indicated and a NULL session is returned, this means the file is local
 */
GF_DownloadSession * gf_dm_sess_new(GF_DownloadManager *dm, const char *url, u32 dl_flags,
                                    gf_dm_user_io user_io,
                                    void *usr_cbk,
                                    GF_Err *error);

/*!
 *\brief download session simple constructor
 *
 *Creates a new download session
 *\param dm The download manager used to create the download session
 *\param url file to retrieve (no PUT/POST yet, only downloading is supported)
 *\param dl_flags combination of session download flags
 *\param user_io \ref gf_dm_user_io callback function for data reception and service messages
 *\param usr_cbk opaque user data passed to callback function
 *\param e error for failure cases
 *\return the session object or NULL if error. If no error is indicated and a NULL session is returned, this means the file is local
 */
GF_DownloadSession *gf_dm_sess_new_simple(GF_DownloadManager * dm, const char *url, u32 dl_flags,
        gf_dm_user_io user_io,
        void *usr_cbk,
        GF_Err *e);

/*!
 *brief downloader session destructor
 *
 *Deletes the download session, cleaning the cache if indicated in the configuration file of the download manager (section "Downloader", key "CleanCache")
 *\param sess the download session
*/
void gf_dm_sess_del(GF_DownloadSession * sess);

/*!
 *\brief aborts downloading
 *
 *Aborts all operations in the session, regardless of its state. The session cannot be reused once this is called.
 *\param sess the download session
 */
void gf_dm_sess_abort(GF_DownloadSession * sess);

/*!
 *\brief sets private data
 *
 *associate private data with the session.
 *\param sess the download session
 *\param private_data the private data
 *\warning the private_data parameter is reserved for bandwidth statistics per service when used in the GPAC terminal.
 */
void gf_dm_sess_set_private(GF_DownloadSession * sess, void *private_data);

/*!
 *\brief gets private data
 *
 *Gets private data associated with the session.
 *\param sess the download session
 *\return the private data
 *\warning the private_data parameter is reserved for bandwidth statistics per service when used in the GPAC terminal.
 */
void *gf_dm_sess_get_private(GF_DownloadSession * sess);

/*!
 *\brief gets last session error
 *
 *Gets the last error that occured in the session
 *\param sess the download session
 *\return the last error
 */
GF_Err gf_dm_sess_last_error(GF_DownloadSession *sess);

/*!
 *\brief is download manager thread dead?
 *
 *Indicates whether the thread has ended
 *\param sess the download session
 */
Bool gf_dm_is_thread_dead(GF_DownloadSession *sess);

/*!
 *\brief fetches data on session
 *
 *Fetches data from the server. This will also performs connections and all needed exchange with server.
 *\param sess the download session
 *\param buffer destination buffer
 *\param buffer_size destination buffer allocated size
 *\param read_size amount of data actually fetched
 *\note this can only be used when the session is not threaded
 */
GF_Err gf_dm_sess_fetch_data(GF_DownloadSession * sess, char *buffer, u32 buffer_size, u32 *read_size);

/*!
 *\brief get mime type as lower case
 *
 *Fetches the mime type of the URL this session is fetching, value will be returned lower case, so application/x-mpegURL will be returned as application/x-mpegurl
 *\param sess the download session
 *\return the mime type of the URL, or NULL if error. You should get the error with \ref gf_dm_sess_last_error
 */
const char *gf_dm_sess_mime_type(GF_DownloadSession * sess);

/*!
 *\brief sets session range
 *
 *Sets the session byte range. This shll be called before processing the session.
 *\param sess the download session
 *\param start_range HTTP download start range in byte
 *\param end_range HTTP download end range in byte
 *\param discontinue_cache If set, forces a new cache entry if byte range are not continuous. Otherwise a single cache entry is used to reconstruct the file
 *\note this can only be used when the session is not threaded
 */
GF_Err gf_dm_sess_set_range(GF_DownloadSession *sess, u64 start_range, u64 end_range, Bool discontinue_cache);
/*!
 *\brief get cache file name
 *
 * Gets the cache file name for the session.
 *\param sess the download session
 *\return the absolute path of the cache file, or NULL if the session is not cached*/
const char *gf_dm_sess_get_cache_name(GF_DownloadSession * sess);

/*!
 * \brief Marks the cache file to be deleted once the file is not used anymore by any session
 * \param dm the download manager
 * \param url The URL associate to the cache entry to be deleted
 */
void gf_dm_delete_cached_file_entry(const GF_DownloadManager * dm, const char * url);

/*!
 * Convenience function
 * \see gf_dm_delete_cached_file_entry
 *\param sess the download session
 * \param url The URL associate to the cache entry to be deleted
 */
void gf_dm_delete_cached_file_entry_session(const GF_DownloadSession * sess, const char * url);

/*!
 * Get a range of a cache entry file
 * \param sess The session
 * \param startOffset The first byte of the request to get
 * \param endOffset The last byte of request to get
 * \return The temporary name for the file created to have a range of the file
 */
const char * gf_cache_get_cache_filename_range( const GF_DownloadSession * sess, u64 startOffset, u64 endOffset );

/*!
 *\brief get statistics
 *
 *Gets download statistics for the session. All output parameters are optional and may be set to NULL.
 *\param sess the download session
 *\param server the remote server address
 *\param path the path on the remote server
 *\param total_size the total size in bytes the file fetched, 0 if unknown.
 *\param bytes_done the amount of bytes received from the server
 *\param bytes_per_sec the average data rate in bytes per seconds
 *\param net_status the session status
 */
GF_Err gf_dm_sess_get_stats(GF_DownloadSession * sess, const char **server, const char **path, u32 *total_size, u32 *bytes_done, u32 *bytes_per_sec, GF_NetIOStatus *net_status);

/*!
 *\brief get start time
 *
 *Gets session start time in UTC. If chunk-transfer is used, the start time is reset at each chunk start
 *\param sess the download session
 *\return UTC start time
 */
u64 gf_dm_sess_get_utc_start(GF_DownloadSession *sess);


/*!
 *\brief fetch session object
 *
 *Fetches the session object (process all headers and data transfer). This is only usable if the session is not threaded
 *\param sess the download session
 *\return the last error in the session or 0 if none*/
GF_Err gf_dm_sess_process(GF_DownloadSession *sess);

/*!
 *\brief fetch session object headers
 *
 *Fetch the session object headers and stops after that. This is only usable if the session is not threaded
 *\param sess the download session
 *\return the last error in the session or 0 if none*/
GF_Err gf_dm_sess_process_headers(GF_DownloadSession * sess);

/*!
 *\brief fetch session status
 *
 *Fetch the session current status
 *\param sess the download session
 *\return the session status*/
u32 gf_dm_sess_get_status(GF_DownloadSession * sess);

/*!
 *\brief Get session resource url
 *
 *Returns the original resource URL associated with the session
 *\param sess the download session
 *\return the associated URL
 */
const char *gf_dm_sess_get_resource_name(GF_DownloadSession *sess);

/*!
 *\brief Get session original resource url
 *
 *Returns the original resource URL before any redirection associated with the session
 *\param sess the download session
 *\return the associated URL
 */
const char *gf_dm_sess_get_original_resource_name(GF_DownloadSession *sess);

#ifndef GPAC_DISABLE_CORE_TOOLS
/*!
 * \brief Download a file over the network using a download manager
 * \param dm The download manager to use, function will use all associated cache ressources
 * \param url The url to download
 * \param filename The filename to download
 * \param start_range start position of a byte range
 * \param end_range end position of a byte range
 * \param redirected_url If not NULL, \p redirected_url will be allocated and filled with the URL after redirection. Caller takes ownership
 * \return GF_OK if everything went fine, an error otherwise
 */
GF_Err gf_dm_wget_with_cache(GF_DownloadManager * dm, const char *url, const char *filename, u64 start_range, u64 end_range, char **redirected_url);

/*!
 * \brief Same as gf_dm_wget_with_cache, but initializes the GF_DownloadManager by itself.
 * This function is deprecated, please use gf_dm_wget_with_cache instead
 * \param url The url to download
 * \param filename The filename to download
 * \param start_range start position of a byte range
 * \param end_range end position of a byte range
 * \param redirected_url If not NULL, \p redirected_url will be allocated and filled with the URL after redirection. Caller takes ownership
 * \return GF_OK if everything went fine, an error otherwise
 */
GF_Err gf_dm_wget(const char *url, const char *filename, u64 start_range, u64 end_range, char **redirected_url);

#endif /* GPAC_DISABLE_CORE_TOOLS */

/*!
 *\brief Reset session
 *
 *Resets the session for new processing of the same url
 *\param sess the download session
 *\return error code if any
 */
GF_Err gf_dm_sess_reset(GF_DownloadSession *sess);

/*!
 * \brief forces the refresh of a cache entry
 * The entry is still allocated in the session.
 * \param sess The session
 * \return a pointer to the entry of session refreshed
 */
DownloadedCacheEntry gf_dm_refresh_cache_entry(GF_DownloadSession *sess);

/*!
 * Tells whether session can be cached on disk.
 * Typically, when request has no content length, it deserves being streamed an cannot be cached
 * (ICY or MPEG-streamed content
 * \param sess The session
 * \return True if a cache can be created
 */
Bool gf_dm_sess_can_be_cached_on_disk(const GF_DownloadSession *sess);


/*!
 * Reassigns session flags and callbacks. This is only possible if the session is not threaded.
 * \param sess The session
 * \param flags The new flags for the session - if flags is 0xFFFFFFFF, existing flags are not modified
 * \param user_io The new callback function
 * \param cbk The new user data to ba used in the callback function
 * \return GF_OK or error
 */
GF_Err gf_dm_sess_reassign(GF_DownloadSession *sess, u32 flags, gf_dm_user_io user_io, void *cbk);

/*!
 * Re-setup an existing, completed session to download a new URL. If same server/port/protocol is used, the same socket will be reused if the session
 * has the GF_NETIO_SESSION_PERSISTENT flag set. This is only possible if the session is not threaded.
 * \param sess The session
 * \param url The new url for the session
 * \return GF_OK or error
 */
GF_Err gf_dm_sess_setup_from_url(GF_DownloadSession *sess, const char *url);

/*
 *\retrieves the HTTP header value for the given name
 *
 *Retrieves the HTTP header value for the given header name.
 *\param sess the current session
 *\param name the target header name
 * \return header value or NULL if not found
 */
const char *gf_dm_sess_get_header(GF_DownloadSession *sess, const char *name);

/*
 *\brief sets download manager max rate per session
 *
 *Sets the maximum rate (per session only at the current time).
 *\param dm the download manager object
 *\param rate_in_bits_per_sec the new rate in bits per sec. If 0, HTTP rate will not be limited
 */
void gf_dm_set_data_rate(GF_DownloadManager *dm, u32 rate_in_bits_per_sec);

/*
 *\brief gets download manager max rate per session
 *
 *Sets the maximum rate (per session only at the current time).
 *\param dm the download manager object
 *\return the rate in bits per sec. If 0, HTTP rate is not limited
 */
u32 gf_dm_get_data_rate(GF_DownloadManager *dm);


/*
 *\brief gets cumultaed download rate for all sessions
 *
 *Gets the cumultated bitrate in of all active sessions.
 *\param dm the download manager object
 *\return the cumulated rate in bits per sec.
 */
u32 gf_dm_get_global_rate(GF_DownloadManager *dm);


/*
 *\brief fetches remote file in memory
 *
 *Fetches remote file in memory.
 *\param url the data to fetch
 *\param out_data output data (allocated by function)
 *\param out_size output data size
 *\param out_mime if not NULL, pointer will contain the mime type (allocated by function)
 *\return error code if any
 */
GF_Err gf_dm_get_file_memory(const char *url, char **out_data, u32 *out_size, char **out_mime);


/*
 *\brief Get header sizes and times stats for the session
 *
 *Get header sizes and times stats for the session
 *\param sess the current session
 *\param req_hdr_size request header size in bytes. May be NULL.
 *\param rsp_hdr_size response header size in bytes. May be NULL.
 *\param connect_time connection time in micro seconds. May be NULL.
 *\param reply_time elapsed time between request sent and response header received, in micro seconds. May be NULL.
 *\param download_time download time since request sent, in micro seconds. May be NULL.
 *\return error code if any
 */
GF_Err gf_dm_sess_get_header_sizes_and_times(GF_DownloadSession *sess, u32 *req_hdr_size, u32 *rsp_hdr_size, u32 *connect_time, u32 *reply_time, u32 *download_time);


/*! @} */

#ifdef __cplusplus
}
#endif


#endif		/*_GF_DOWNLOAD_H_*/

